package com.cloud.lan.service.impl;

import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloud.lan.aop.RequestClient;
import com.cloud.lan.api.dto.req.LanUserReq;
import com.cloud.lan.api.dto.req.WxLoginRequest;
import com.cloud.lan.api.dto.resp.BaseUserInfoResp;
import com.cloud.lan.api.dto.resp.LanUserResp;
import com.cloud.lan.api.dto.resp.OnLoginResp;
import com.cloud.lan.dao.LanUserMapper;
import com.cloud.lan.dao.LanUserWhiteListMapper;
import com.cloud.lan.dao.LanUserWxMapper;
import com.cloud.lan.dao.SmsMessageMapper;
import com.cloud.lan.entity.base.BaseResponse;
import com.cloud.lan.entity.base.exceptions.BaseException;
import com.cloud.lan.entity.enums.LanError;
import com.cloud.lan.entity.enums.RedisKeyEnum;
import com.cloud.lan.entity.model.dataobject.*;
import com.cloud.lan.service.ILanUserService;
import com.cloud.lan.service.aliyun.AliyunConfig;
import com.cloud.lan.service.aliyun.AliyunService;
import com.cloud.lan.service.tx.wx.WxInitService;

import org.springframework.stereotype.Service;

import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

import static com.cloud.lan.entity.utils.ConstantInterface.Enum.ConstantNumber.ONE;
import static com.cloud.lan.entity.utils.ConstantInterface.Enum.ConstantNumber.ZERO;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author xyqierkang@163.com
 * @since 2021-12-06
 */
@Slf4j
@Service
public class LanUserServiceImpl extends ServiceImpl<LanUserMapper, LanUserDO> implements ILanUserService {

    @Resource
    WxInitService wxInitService;
    @Resource
    LanUserMapper lanUserMapper;
    @Resource
    RedisService redisService;
    @Resource
    SmsMessageMapper smsMessageMapper;
    @Resource
    LanUserWxMapper lanUserWxMapper;
    @Resource
    AliyunService aliyunService;
    @Resource
    AliyunConfig aliyunConfig;
    @Resource
    LanUserWhiteListMapper lanUserWhiteListMapper;

    @Override
    @RequestClient
    public BaseResponse<OnLoginResp> onLogin(WxLoginRequest request) {
        try {
            WxMaJscode2SessionResult result = wxInitService.getSession(request.getWxAuthCode());
            String openId = result.getOpenid();
            String unionId = result.getUnionid();
            String token = result.getSessionKey();
            LambdaQueryWrapper<LanUserDO> wxQw = new LambdaQueryWrapper<>();
            wxQw.eq(LanUserDO::getOpenId, openId);
            LanUserDO lanUserDO = lanUserMapper.selectOne(wxQw);
            Long userId = Long.parseLong(RandomUtil.randomNumbers(10));
            // 初始化 如果 LanUserWxDO没有sessionKey 新增
            LambdaQueryWrapper<LanUserWxDO> luWd = new LambdaQueryWrapper<>();
            luWd.eq(LanUserWxDO::getToken, token);
            if (lanUserWxMapper.selectCount(luWd).equals(ZERO.getKey())) {
                LanUserWxDO userWxDO = new LanUserWxDO();
                userWxDO.setToken(token).setOpenId(openId);
                lanUserWxMapper.insert(userWxDO);
            }
            if (lanUserDO == null) {
                LanUserDO userDO = new LanUserDO();
                userDO.setOpenId(openId).setUserId(userId).setUnionId(unionId);
                lanUserMapper.insert(userDO);
            } else {
                LambdaUpdateWrapper<LanUserDO> luw = new LambdaUpdateWrapper<>();
                luw.set(LanUserDO::getUpdateTime, DateUtil.now());
                luw.set(LanUserDO::getUnionId, unionId);
                luw.eq(LanUserDO::getId, lanUserDO.getId());
                lanUserMapper.update(new LanUserDO(), luw);
            }
            log.info("#微信认证 登录 用户信息 onLogin token {}", token);
            return BaseResponse.success(token);
        } catch (Exception e) {
            log.error("微信认证 用户信息 异常:", e);
            return BaseResponse.error(e.getMessage());
        }
    }

    @Override
    public BaseResponse<Object> onUserInfo(WxLoginRequest request) {
        try {
            log.info("#微信 登录 用户信息 {}", request);
            LambdaQueryWrapper<LanUserWxDO> wxQw = new LambdaQueryWrapper<>();
            wxQw.eq(LanUserWxDO::getToken, request.getToken());
            LanUserWxDO lanUserWxDO = lanUserWxMapper.selectOne(wxQw);
            if (lanUserWxDO == null) {
                log.error("用户登录失效");
                return BaseResponse.error("用户登录失效");
            }
            LanUserResp lanUserResp = new LanUserResp();
            LambdaQueryWrapper<LanUserDO> qw = new LambdaQueryWrapper<>();
            qw.eq(LanUserDO::getOpenId, lanUserWxDO.getOpenId());
            LanUserDO lanUserDO = lanUserMapper.selectOne(qw);
            if (ObjectUtil.isNotEmpty(lanUserDO)) {
                String encryptedData = URLEncoder.encode(request.getEncryptedData(), "UTF-8").
                        replace("%2B", "+").
                        replace("%3D", "=").
                        replace("%2F", "/");
                String iv = URLEncoder.encode(request.getIv(), "UTF-8").
                        replace("%2B", "+").
                        replace("%3D", "=").
                        replace("%2F", "/");
                String userInit = wxInitService.encryptedData(request.getToken(), encryptedData, iv);
                // 解密用户信息
                WxMaUserInfo userInfo = JSONUtil.toBean(userInit, WxMaUserInfo.class);
                if (ObjectUtil.isNotEmpty(userInfo)) {
                    lanUserDO.setWxHead(userInfo.getAvatarUrl())
                            .setWxName(userInfo.getNickName()).setOpenId(userInfo.getOpenId());
                    if (StrUtil.isEmpty(lanUserDO.getFirstEp())) {
                        lanUserDO.setFirstEp(request.getEp());
                    }
                    lanUserMapper.updateById(lanUserDO);
                }
                lanUserResp = getUserInfo(request.getToken());
            }
            return BaseResponse.success(lanUserResp);
        } catch (Exception e) {
            log.error("微信登录 用户信息 异常:", e);
            return BaseResponse.error("微信登录 用户信息 异常" + e.getMessage());
        }
    }

    @Override
    @RequestClient
    public BaseResponse<Object> get(String token) {
        log.info("#查询 用户信息 {}", token);
        LanUserResp lanUserResp = getUserInfo(token);
        return BaseResponse.success(lanUserResp);
    }

    @Override
    public BaseResponse<Object> add(LanUserReq request) {
        log.info("#新增 用户信息 {}", request);
        LanUserDO model = new LanUserDO();
        BeanUtil.copyProperties(request, model);
        model.setUserId(Long.parseLong(RandomUtil.randomNumbers(10)));
        lanUserMapper.insert(model);
        return BaseResponse.success();
    }

    @Override
    public BaseResponse<Object> update(LanUserReq request) {
        log.info("#更新 update 用户信息 {}", request);
        LambdaQueryWrapper<LanUserWxDO> wxQw = new LambdaQueryWrapper<>();
        wxQw.eq(LanUserWxDO::getToken, request.getToken());
        LanUserWxDO lanUserWxDO = lanUserWxMapper.selectOne(wxQw);
        LanUserDO lanUserDO = lanUserMapper.selectOne(new LambdaQueryWrapper<LanUserDO>().eq(LanUserDO::getOpenId, lanUserWxDO.getOpenId()));
        LanUserDO model = new LanUserDO();
        BeanUtil.copyProperties(request, model);
        model.setUpdateTime(DateUtil.date());
        model.setUserBirthday(lanUserDO.getUserBirthday() != null ? null : model.getUserBirthday());
        lanUserMapper.update(model, new
                LambdaQueryWrapper<LanUserDO>()
                .eq(LanUserDO::getOpenId, lanUserWxDO.getOpenId()));
        return BaseResponse.success();
    }

    @Override
    public BaseResponse<Object> authLogin(LanUserReq request) {
        log.info("#更新 authLogin 用户信息 {}", request);
//        String imgCode = redis.get("img" + request.getToken());
//        if (StrUtil.isEmpty(imgCode)) {
//            return BaseResponse.error("验证码过期!请重试");
//        }
//        if (!imgCode.equals(request.getImgCode())) {
//            return BaseResponse.error("图片验证码错误!请重试");
//        }
//        校验手机验证码
        String smsCode = redisService.get(RedisKeyEnum.SMS_CODE.key(request.getToken()));
        if (StrUtil.isEmpty(smsCode)) {
            log.error("{} token {}", LanError.LAN_010004, request.getToken());
            throw new BaseException(LanError.LAN_010004);
        }
        if (!smsCode.equals(request.getSmsCode())) {
            log.error("{} token {}", LanError.LAN_010005, request.getToken());
            throw new BaseException(LanError.LAN_010005);
        }
        // 验证码只有一次机会
        redisService.delete(RedisKeyEnum.SMS_CODE.key(request.getToken()));
        redisService.delete(RedisKeyEnum.IMG_CODE.key(request.getToken()));
        LambdaQueryWrapper<LanUserWxDO> wxQw = new LambdaQueryWrapper<>();
        wxQw.eq(LanUserWxDO::getToken, request.getToken());
        LanUserWxDO lanUserWxDO = lanUserWxMapper.selectOne(wxQw);
        LanUserDO model = new LanUserDO();
        BeanUtil.copyProperties(request, model);
        model.setUpdateTime(DateUtil.date());
        lanUserMapper.update(model, new
                LambdaQueryWrapper<LanUserDO>()
                .eq(LanUserDO::getOpenId, lanUserWxDO.getOpenId()));
        return BaseResponse.success();
    }

    @Override
    public BaseResponse<Object> getSmsCode(LanUserReq request) {
        String code = RandomUtil.randomNumbers(4);
        Map<String, Object> smsMap = new HashMap<>();
        String key = request.getToken().replaceAll(" ", "");
        smsMap.put("code", code);
        LambdaQueryWrapper<LanUserWxDO> wxQw = new LambdaQueryWrapper<>();
        wxQw.eq(LanUserWxDO::getToken, request.getToken());
        LanUserWxDO lanUserWxDO = lanUserWxMapper.selectOne(wxQw);
        if (lanUserWxDO == null) {
            return BaseResponse.error("token查询空!");
        }
        SendSmsResponse result = aliyunService.smsSend(request.getUserPhone(), smsMap);
        // 记录发送日志
        SmsMessageDO smsMessageDO =
                new SmsMessageDO().setUserPhone(request.getUserPhone())
                        .setResultMsg(JSONUtil.toJsonStr(result.body))
                        .setResultState(String.valueOf(0)).setResultType(0).setSendContent(JSONUtil.toJsonStr(smsMap))
                        .setOpenId(lanUserWxDO.getOpenId());
        smsMessageMapper.insert(smsMessageDO);
        if (!result.body.code.equals(BaseResponse.OK_MSG)) {
            log.error("{} token {}", LanError.LAN_020007, request.getToken());
            throw new BaseException(LanError.LAN_020007);
        }
        redisService.setEx(RedisKeyEnum.SMS_CODE.key(key), 300, code);
        return BaseResponse.success();
    }

    @Override
    public LanUserResp getUserInfo(String token) {
        LambdaQueryWrapper<LanUserWxDO> wxQw = new LambdaQueryWrapper<>();
        wxQw.eq(LanUserWxDO::getToken, token);
        LanUserWxDO lanUserWxDO = lanUserWxMapper.selectOne(wxQw);
        LanUserResp lanUserResp = new LanUserResp();
        if (lanUserWxDO == null) {
            log.error("getUserInfo查询用户空 {}", token);
            return lanUserResp;
        }
        LambdaQueryWrapper<LanUserDO> qw = new LambdaQueryWrapper<>();
        qw.eq(LanUserDO::getOpenId, lanUserWxDO.getOpenId());
        LanUserDO lanUserDO = lanUserMapper.selectOne(qw);
        BeanUtil.copyProperties(lanUserDO, lanUserResp);
        lanUserResp.setUserPhone(DesensitizedUtil.mobilePhone(lanUserResp.getUserPhone()));
        lanUserResp.setUserName(StrUtil.isNotEmpty(lanUserDO.getUserName()) ? lanUserDO.getUserName() : lanUserDO.getWxName());
        lanUserResp.setUserHead(StrUtil.isNotEmpty(lanUserDO.getUserHead()) ? lanUserDO.getUserHead() : lanUserDO.getWxHead());
        return lanUserResp;
    }

    @Override
    public String getJsonObj(String method) {
        String content = redisService.get(method);
        if (StrUtil.isEmpty(content)) {
            String url = aliyunConfig.getOss().getCdnDomain() + "/json/api/" + method + ".json";
            return HttpUtil.get(url);
        }
        return content;
    }

    @Override
    public BaseResponse<BaseUserInfoResp> baseUserInfo(LanUserReq request) {
        LambdaQueryWrapper<LanUserWxDO> wxQw = new LambdaQueryWrapper<>();
        wxQw.eq(LanUserWxDO::getToken, request.getToken());
        LanUserWxDO lanUserWxDO = lanUserWxMapper.selectOne(wxQw);
        String openId = lanUserWxDO.getOpenId();
        BaseUserInfoResp baseUserInfoResp = new BaseUserInfoResp();
        LambdaQueryWrapper<LanUserDO> luQw = new LambdaQueryWrapper<>();
        luQw.eq(LanUserDO::getOpenId, openId);
        LanUserDO lanUserDO = lanUserMapper.selectOne(luQw);
        LambdaQueryWrapper<LanUserWhiteListDO> luwQw = new LambdaQueryWrapper<>();
        luwQw.eq(LanUserWhiteListDO::getOpenId, openId);
        luwQw.orderByDesc(LanUserWhiteListDO::getCreateTime).last("limit 1");
        LanUserWhiteListDO lanUserWhiteListDO = lanUserWhiteListMapper.selectOne(luwQw);
        // 判断用户是否是新用户
        if (lanUserDO == null) {
            return BaseResponse.success(baseUserInfoResp);
        }
        // 校验用户是否绑定手机号
        if (StrUtil.isNotEmpty(lanUserDO.getUserPhone())) {
            baseUserInfoResp.setAuthPhone(Boolean.FALSE);
        }
        // 校验用户是否需要微信授权 目前根据头像校验
        if (StrUtil.isNotEmpty(lanUserDO.getWxHead())) {
            baseUserInfoResp.setAuthorize(Boolean.FALSE);
        }
        if (ObjectUtil.isNotEmpty(lanUserWhiteListDO)) {
            baseUserInfoResp.setUserWhite(Boolean.TRUE);
            baseUserInfoResp.setIsActive(lanUserWhiteListDO.getIsActive());
        }
        return BaseResponse.success(baseUserInfoResp);
    }

}
